package com.ruoyi.framework.web.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.WechatAccessToken;
import com.ruoyi.common.core.domain.model.WechatUserInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SaTokenUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.LoginSessionManager;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.WechatLoginService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.config.OkHttpConfig;
import com.ruoyi.common.config.WechatConfig;

import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序登录服务实现
 *
 * @author ruoyi
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatLoginServiceImpl implements WechatLoginService {
    
    private final WechatConfig wechatConfig;
    private final OkHttpConfig okHttpConfig;
    private final OkHttpClient httpClient;
    private final ISysUserService userService;
    private final SysPermissionService permissionService;
    private final RedisCache redisCache;
    private final LoginSessionManager loginSessionManager;
    
    private static final String WECHAT_USER_TEMP_KEY = "wechat:temp:";
    private static final int TEMP_EXPIRE_TIME = 15;
    
    @Override
    public Object wechatMiniLogin(String code) {
        try {
            WechatAccessToken session = getMiniSession(code);
            String openid = session.getOpenid();
            SysUser existUser = userService.selectUserByWechatOpenid(openid);
            
            if (existUser != null) {
                WechatUserInfo wechatUser = buildWechatUserInfo(openid, existUser);
                return performLogin(existUser, wechatUser);
            } else {
                WechatUserInfo wechatUser = buildTempWechatUserInfo(openid);
                storeWechatUserTemp(wechatUser);
                return wechatUser;
            }
        } catch (Exception e) {
            log.error("微信小程序登录失败", e);
            AsyncManager.me().execute(AsyncFactory.recordLogininfor("微信小程序登录", Constants.LOGIN_FAIL, "微信小程序登录异常"));
            throw new ServiceException("微信小程序登录失败，请稍后重试");
        }
    }
    
    @Override
    @Transactional
    public String bindWechatAccount(String openid, String phoneNumber) {
        WechatUserInfo wechatUser = getWechatUserTemp(openid);
        if (wechatUser == null) {
            throw new ServiceException("微信登录信息已过期，请重新登录");
        }
        
        SysUser phoneUser = userService.selectUserByPhoneNumber(phoneNumber);
        if (phoneUser != null) {
            if (StringUtils.isNotEmpty(phoneUser.getWechatOpenid())) {
                throw new ServiceException("该手机号已绑定其他微信账号，请先解绑");
            }
            return bindWechatToExistingAccount(phoneUser, wechatUser);
        } else {
            return createNewAccountAndBind(phoneNumber, wechatUser);
        }
    }
    
    @Override
    @Transactional
    public String forceRebindWechat(String openid, String phoneNumber) {
        SysUser targetUser = userService.selectUserByPhoneNumber(phoneNumber);
        if (targetUser == null) {
            throw new ServiceException("手机号不存在");
        }
        
        unbindWechatAccount(targetUser.getUserId());
        
        WechatUserInfo wechatUser = getWechatUserTemp(openid);
        if (wechatUser == null) {
            throw new ServiceException("微信登录信息已过期，请重新登录");
        }
        
        return bindWechatToExistingAccount(targetUser, wechatUser);
    }
    
    @Override
    @Transactional
    public void unbindWechatAccount(Long userId) {
        SysUser user = new SysUser();
        user.setUserId(userId);
        user.setWechatOpenid(null);
        user.setBindTime(null);
        userService.updateUser(user);
        log.info("用户 {} 解绑微信账号成功", userId);
    }
    
    @Override
    public boolean isWechatBound(Long userId) {
        SysUser user = userService.selectUserById(userId);
        return user != null && StringUtils.isNotEmpty(user.getWechatOpenid());
    }
    
    private WechatUserInfo buildWechatUserInfo(String openid, SysUser user) {
        WechatUserInfo wechatUser = new WechatUserInfo();
        wechatUser.setOpenid(openid);
        wechatUser.setNickname(user.getNickName());
        wechatUser.setHeadimgurl(user.getAvatar());
        return wechatUser;
    }
    
    private WechatUserInfo buildTempWechatUserInfo(String openid) {
        WechatUserInfo wechatUser = new WechatUserInfo();
        wechatUser.setOpenid(openid);
        wechatUser.setNickname("微信用户");
        wechatUser.setHeadimgurl("");
        return wechatUser;
    }
    
    private String performLogin(SysUser user, WechatUserInfo wechatUser) {
        try {
            updateUserInfo(user, wechatUser);
            
            Set<String> permissions = permissionService.getMenuPermission(user);
            LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissions);
            
            StpUtil.login(user.getUserId());
            String tokenValue = StpUtil.getTokenValue();
            loginUser.setToken(tokenValue);
            
            setLoginInfo(loginUser);
            loginSessionManager.storeLoginUserToSession(loginUser, tokenValue);
            
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, "微信登录成功"));
            clearWechatUserTemp(wechatUser.getOpenid());
            
            return tokenValue;
        } catch (Exception e) {
            log.error("微信登录失败", e);
            throw new ServiceException("登录失败，请稍后重试");
        }
    }
    
    private void updateUserInfo(SysUser user, WechatUserInfo wechatUser) {
        boolean needUpdate = false;
        
        if (StringUtils.isNotEmpty(wechatUser.getNickname()) && 
            !wechatUser.getNickname().equals(user.getNickName())) {
            user.setNickName(wechatUser.getNickname());
            needUpdate = true;
        }
        
        if (StringUtils.isNotEmpty(wechatUser.getHeadimgurl()) && 
            !wechatUser.getHeadimgurl().equals(user.getAvatar())) {
            user.setAvatar(wechatUser.getHeadimgurl());
            needUpdate = true;
        }
        
        if (needUpdate) {
            userService.updateUser(user);
        }
    }
    
    private void setLoginInfo(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        String ipAddr = IpUtils.getIpAddr();
        loginUser.setIpaddr(ipAddr);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ipAddr));
        
        String userAgentString = ServletUtils.getRequest().getHeader("User-Agent");
        loginUser.setBrowser(UserAgentUtils.parseBrowserName(userAgentString));
        loginUser.setOs(UserAgentUtils.parseOsName(userAgentString));
    }
    
    private void storeWechatUserTemp(WechatUserInfo wechatUser) {
        String key = WECHAT_USER_TEMP_KEY + wechatUser.getOpenid();
        redisCache.setCacheObject(key, wechatUser, TEMP_EXPIRE_TIME, TimeUnit.MINUTES);
    }
    
    private WechatUserInfo getWechatUserTemp(String openid) {
        String key = WECHAT_USER_TEMP_KEY + openid;
        return redisCache.getCacheObject(key);
    }
    
    private void clearWechatUserTemp(String openid) {
        String key = WECHAT_USER_TEMP_KEY + openid;
        redisCache.deleteObject(key);
    }
    
    private String bindWechatToExistingAccount(SysUser user, WechatUserInfo wechatUser) {
        user.setWechatOpenid(wechatUser.getOpenid());
        user.setBindTime(new Date());
        
        if (StringUtils.isNotEmpty(wechatUser.getNickname())) {
            user.setNickName(wechatUser.getNickname());
        }
        if (StringUtils.isNotEmpty(wechatUser.getHeadimgurl())) {
            user.setAvatar(wechatUser.getHeadimgurl());
        }
        
        userService.updateUser(user);
        log.info("用户 {} 绑定微信账号成功", user.getUserName());
        
        return performLogin(user, wechatUser);
    }
    
    private String createNewAccountAndBind(String phoneNumber, WechatUserInfo wechatUser) {
        SysUser newUser = new SysUser();
        newUser.setUserName(phoneNumber);
        newUser.setNickName(StringUtils.isNotEmpty(wechatUser.getNickname()) ? wechatUser.getNickname() : "微信用户");
        newUser.setPhonenumber(phoneNumber);
        newUser.setPassword(SaTokenUtils.encryptPassword("123456"));
        newUser.setStatus("0");
        newUser.setWechatOpenid(wechatUser.getOpenid());
        newUser.setBindTime(new Date());
        
        if (StringUtils.isNotEmpty(wechatUser.getHeadimgurl())) {
            newUser.setAvatar(wechatUser.getHeadimgurl());
        }
        
        newUser.setRoleIds(new Long[]{2L});
        userService.insertUser(newUser);
        
        log.info("创建新用户 {} 并绑定微信账号成功", newUser.getUserName());
        return performLogin(newUser, wechatUser);
    }
    
    private WechatAccessToken getMiniSession(String code) {
        String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", 
            wechatConfig.getMiniSessionUrl(), wechatConfig.getAppId(), 
            wechatConfig.getAppSecret(), code);
        
        String response = httpGet(url);
        WechatAccessToken sessionResult = JSON.parseObject(response, WechatAccessToken.class);
        
        if (sessionResult.getErrcode() != null && sessionResult.getErrcode() != 0) {
            throw new ServiceException("获取微信小程序session失败: " + sessionResult.getErrmsg());
        }
        return sessionResult;
    }
    
    private String httpGet(String url) {
        Request request = new Request.Builder()
            .url(url)
            .header("User-Agent", okHttpConfig.getUserAgent())
            .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (response.body() == null) {
                throw new ServiceException("HTTP请求无响应内容");
            }
            return response.body().string();
        } catch (Exception e) {
            log.error("HTTP请求失败: {}", url, e);
            throw new ServiceException("HTTP请求失败: " + e.getMessage());
        }
    }
}